package controle;


import projet_ascenseur.Ascenseur;
import gui.GUIFrame;
import stock.Stock;
import requetes.Deplacement;

public class Controle 
{

	public static int le_delai_pas = 100;
	public static int le_delai_arret_cabine = 5;
	public static int le_delai_ouverture_porte = 5;


	private static int MONTEE 			= 1;
	private static int DESCENTE 			= 2;
	private static int ARRET			= 3;
	private static int PORTE_OUVERTE		= 4;
	private static int URGENCE			= 5;
	
	
	private Ascenseur ascenseur;
	private GUIFrame gui;
	private Stock stock;


	private int nb_etages;
	private int etage;                        // etage courant
	private int sens;                 // sens préférentiel de déplacement de l'ascenseur: soit MONTEE, soit DESCENTE
	private int etat;                // etat courant de l'ascenseur: arerete, portes ouvertes, en train de monter, etc...
	private Deplacement deplacement_courant;  // requete de deplacement en train d'etre traitee.

	private GUIFrame fenetre;

	public Controle( int nb_etages, int Hauteur_etage )
	{
		etage = 0;
		sens  = MONTEE;
		etat  = ARRET;

		this.nb_etages = nb_etages;

		fenetre = new GUIFrame(nb_etages, Hauteur_etage);
		fenetre.assigne_ordonnanceur( this );
		
		ascenseur = new Ascenseur(nb_etages, le_delai_pas, Hauteur_etage, le_delai_arret_cabine, le_delai_ouverture_porte );
		ascenseur.assigne_ordonnanceur( this );

		stock = new Stock();
	}

	/*
	 * Utilisé par GUI pour signaler une demande.
	 * - Si d.etat = INDEFINI: il s'agit d'une demande effectuée à l'interieur
	 * 			de l'ascenseur. d.etage représente alors l'étage demandé.
	 * 			On effectue un prétraitement dessus: si l'étage demandé est
	 * 			au dessus de l'étage courant, il s'agit d'une demande de MONTEE.
	 * 			Sinon c'est une descente.
	 * Sinon on ne modifie pas la demande de deplacement.
	 * - On ajoute la demande de déplacement dans le stock
	 * - On appel la fonction prochain_deplacement() pour mettre à jour
	 * 			le déplacement courant.
	 * 
	 */
	public void demander(Deplacement d )
	{
		
		if( d==null ){

			System.out.println("Aucun deplacement a faire");
			return;
		}
		
		/*
		 * Si on est en arret d'urgence, on ignore toutes les demandes.
		 * De même, si les portes sont ouvertes et que quelqu'un appelle l'ascenseur à l'étage courant
		 * on ignore la demande.
		 */
		if(etat == URGENCE)
			return;

		if( etage == d.etage && (PORTE_OUVERTE == etat || ARRET == etat) ){

			fenetre.ajouteMessage("Ouverture des portes");
			ascenseur.ouverture_porte();

			return;

		}
			
		fenetre.allumer_bouton( d );

		if(d.sens == Deplacement.INDEFINI) {

			d.boutons = Deplacement.INDEFINI;

			if(etage - d.etage > 0) {
				d.sens = Deplacement.DESCENTE;
			} else if(etage - d.etage < 0) {
				d.sens = Deplacement.MONTEE;
			} else {
				/*
				 * Si la demande est effectuée pour l'etage auquel on se trouve,
				 * on l'ajoute dans le stock dans le sens inverse du déplacement de l'ascenseur
				 * afin d'eviter que celui-ci ne s'arrete en pleine course.
				 */
				if(sens == MONTEE)
					d.sens = Deplacement.DESCENTE;
				else
					d.sens = Deplacement.MONTEE;
			}
		}
		else if( d.sens == Deplacement.MONTEE )
			d.boutons = Deplacement.MONTEE;
		else if( d.sens == Deplacement.DESCENTE )
			d.boutons = Deplacement.DESCENTE;


		stock.ajouterRequete(d);
		prochain_deplacement();
	}
	
	/*
	 * En arret d'urgence:
	 * - On se met en etat URGENCE
	 * - On vide le stock
	 * - On arrete l'ascensceur.
	 * Il faudra appuyer une deuxieme fois sur le bouton "Arret d'urgence" pour
	 * debloquer l'ascenseur.
	 */
	public void arret_urgence()
	{
		if(etat == URGENCE){
			ascenseur.finArretUrgence();
		}else {
			etat = URGENCE;
			stock.viderStock();
			deplacement_courant = null;
	
			eteindre_boutons();

			new Thread(){
			
				public void run(){
			
					ascenseur.arreter();

				}

			}.start();
		}
	}

	private void eteindre_boutons(){
	

		int i;
		
		fenetre.eteindre_bouton( new Deplacement(Deplacement.INDEFINI, 0) );
		fenetre.eteindre_bouton( new Deplacement(Deplacement.MONTEE, 0) );
		fenetre.eteindre_bouton( new Deplacement(Deplacement.INDEFINI, nb_etages-1) );
		fenetre.eteindre_bouton( new Deplacement(Deplacement.DESCENTE, nb_etages-1) );
		for( i=1; i<nb_etages-2; i++)
		{
				fenetre.eteindre_bouton( new Deplacement(Deplacement.INDEFINI, i) );
				fenetre.eteindre_bouton( new Deplacement(Deplacement.MONTEE, i) );
				fenetre.eteindre_bouton( new Deplacement(Deplacement.DESCENTE, i) );
		}

	}

	/*
	 * Utilisé par la partie ascenseur pour signaler que l'on atteint un nouvel étage
	 * - On décrémente ou incrémente etage en fonction du sens de déplacement
	 * - On teste si on est à l'étage juste en dessous (ou juste au dessus selon le sens de déplacement).
	 * 			Si c'est le cas, on appelle la methode arreter_prochain_niveau();
	 * - Si on est arrivé au bon étage:
	 * 			- On ouvre les portes de l'ascensceur (et on passe dans l'etat PORTE_OUVERTE)
	 * 			- On supprime la requete courante, car elle vient d'etre effectuee.
	 */
	public void signaler_etage()
	{

		if(sens == MONTEE){
			etage++;
			if(etage == deplacement_courant.etage - 1)
				ascenseur.arreter_prochain_niveau();
		} else {
			etage--;
			if(etage == deplacement_courant.etage + 1)
				ascenseur.arreter_prochain_niveau();
		}
		
		fenetre.changePosition( etage );

		if((etage == deplacement_courant.etage) && (etat == ARRET) ) {

			if( (deplacement_courant.boutons & Deplacement.INDEFINI) != 0 )
				fenetre.eteindre_bouton( new Deplacement( Deplacement.INDEFINI, etage ) );

			if( (deplacement_courant.boutons & Deplacement.MONTEE) != 0 )
				fenetre.eteindre_bouton( new Deplacement( Deplacement.MONTEE, etage ) );

			if( (deplacement_courant.boutons & Deplacement.DESCENTE) != 0 )
				fenetre.eteindre_bouton( new Deplacement( Deplacement.DESCENTE, etage ) );

			ascenseur.ouverture_porte();
			fenetre.ajouteMessage("Ouverture des portes");
			etat = PORTE_OUVERTE;
			deplacement_courant = null;
		}
	}
	
	/*
	 * Permet à l'ascenseur d'indiquer que les portes se sont refermées.
	 * On peut alors traiter la requete suivante.
	 */
	public void signal_porte_fermee()
	{
		etat = ARRET;
		prochain_deplacement();
	}
		
	public int etage()
	{
		return etage;
	}
	
	public int etat()
	{
		return etat;
	}
	
	public int sens()
	{
		return sens;
	}
	
	/*
	 * Permet de mettre à jour deplacement_courant.
	 */
	protected void prochain_deplacement() 
	{
		/*
		 * Si les portes sont ouvertes, on ne bouge pas l'ascenseur, s'il vous plaît.
		 */
		if(etat == PORTE_OUVERTE) 
			return;
		
		/*
		 * On met à jour déplacement_courant en récupérant la prochaine requete à traiter
		 * dans le sens de l'ascenseur. Si il n'y en a pas, on change de sens et rebelotte.
		 * Si aucune requete dans les deux sens, ben on fait rien.
		 */
		if(deplacement_courant != null)
			stock.ajouterRequete(deplacement_courant);
		
		if(sens == MONTEE)
			deplacement_courant = stock.prochain(Deplacement.MONTEE);
		else
			deplacement_courant = stock.prochain(Deplacement.DESCENTE);
		
		if(deplacement_courant == null) {


		if(sens == MONTEE) {

				sens = DESCENTE;
				deplacement_courant = stock.prochain(Deplacement.DESCENTE);
			} else {

				sens = MONTEE;
				deplacement_courant = stock.prochain(Deplacement.MONTEE);
			}
		}
		
		if(deplacement_courant == null){
			System.out.println("[Controle] Pas de requete"); 
			return;

		} else {
			if( deplacement_courant.etage - etage > 0 )
				sens = MONTEE;
			else
				sens = DESCENTE;
		}
		/*
		 * Si l'ascenseur est à l'arrêt, il faut lui dire de repartir, sauf si il se trouve deja
		 * à l'etage demande, auquel cas il suffit d'ouvrir les portes.
		 */
		if(etat == ARRET) {
			if(deplacement_courant.etage == etage) {
				etat = PORTE_OUVERTE;
				ascenseur.ouverture_porte();
				fenetre.ajouteMessage("Ouverture des portes");
			} else if(sens == MONTEE){ 
				ascenseur.monter();
				fenetre.ajouteMessage("On monte...");
			 }else{
				ascenseur.descendre();
				fenetre.ajouteMessage("On descend...");
			 }
		}

		/*
		 * Si le prochain etage est l'etage suivant (dans le sens de la marche de l'ascenseur),
		 * on lui indique de s'arreter au prochain niveau. 	
		 */
		if(deplacement_courant.etage == etage + 1)
			ascenseur.arreter_prochain_niveau();
		else if(deplacement_courant.etage == etage - 1)
			ascenseur.arreter_prochain_niveau();
		
	}

	public void setOuverture( int p, int max ){

		fenetre.setOuverture(p,max);

	}

	public void setSens( int sens ){

		fenetre.setSens( sens );

	}
	
}
